use actix_files as fs;
use actix_web::web::{self, ServiceConfig};
use actix_web::{get, HttpResponse};
use actix_web::middleware::Compress;
use shuttle_actix_web::ShuttleActixWeb;
use std::{fs as std_fs, path::PathBuf}; 
use serde::Serialize;
use chrono;

#[derive(Serialize)]
struct FileInfo {
    name: String,
    r#type: String,  // "file" or "directory"
    path: String,
    mtime: String,
}

#[get("/list:/{path:.*}")]
async fn list_directory(path: web::Path<String>) -> HttpResponse {
    let requested_path = path.into_inner();
    let full_path = PathBuf::from("static").join(&requested_path);

    match std_fs::metadata(&full_path) {
        Ok(metadata) => {
            if metadata.is_dir() {
                match std_fs::read_dir(&full_path) {
                    Ok(entries) => {
                        let mut dir_contents = Vec::new();
                        let root_metadata = metadata.clone();  // Clone metadata before loop
                        for entry in entries.flatten() {
                            if let Ok(file_type) = entry.file_type() {
                                let file_name = entry.file_name().to_string_lossy().into_owned();
                                let file_path = PathBuf::from(&requested_path).join(&file_name);
                                let file_stats = std_fs::metadata(entry.path()).unwrap_or(root_metadata.clone());
                                
                                dir_contents.push(FileInfo {
                                    name: file_name,
                                    r#type: if file_type.is_dir() { 
                                        "directory".to_string() 
                                    } else { 
                                        "file".to_string() 
                                    },
                                    path: file_path.to_string_lossy().into_owned(),
                                    mtime: file_stats.modified()
                                        .unwrap_or(std::time::SystemTime::UNIX_EPOCH)
                                        .duration_since(std::time::UNIX_EPOCH)
                                        .map(|d| {
                                            chrono::DateTime::<chrono::Utc>::from(
                                                std::time::UNIX_EPOCH + d
                                            ).to_rfc3339()
                                        })
                                        .unwrap_or_default(),
                                       
                                });
                            }
                        }
                        HttpResponse::Ok().json(dir_contents)
                    }
                    Err(_) => HttpResponse::InternalServerError().json(
                        vec![FileInfo {
                            name: "error".to_string(),
                            r#type: "error".to_string(),
                            path: "".to_string(),
                            mtime: "".to_string(),
                        }]
                    ),
                }
            } else {
                HttpResponse::Ok().json(FileInfo {
                    name: full_path.file_name()
                        .unwrap_or_default()
                        .to_string_lossy()
                        .into_owned(),
                    r#type: "file".to_string(),
                    path: requested_path,
                    mtime: metadata.modified()
                        .unwrap_or(std::time::SystemTime::UNIX_EPOCH)
                        .duration_since(std::time::UNIX_EPOCH)
                        .unwrap_or_default()
                        .as_secs()
                        .to_string(),
                })
            }
        }
        Err(_) => HttpResponse::NotFound().json(
            vec![FileInfo {
                name: "error".to_string(),
                r#type: "error".to_string(),
                path: "".to_string(),
                mtime: "".to_string(),
            }]
        ),
    }
}

#[shuttle_runtime::main]
async fn main() -> ShuttleActixWeb<impl FnOnce(&mut ServiceConfig) + Send + Clone + 'static> {
    let config = move |cfg: &mut ServiceConfig| {
        // Add compression middleware using app_data
        cfg.app_data(web::JsonConfig::default().limit(4096)); // Optional: configure JSON payload size
        
        cfg.service(
            web::scope("")
                // Add compression middleware to the scope
                .wrap(Compress::default())
                // Add cache control headers
                .wrap(actix_web::middleware::DefaultHeaders::new()
                    .add(("Cache-Control", "public, max-age=3600")))
                .service(list_directory)
                .service(fs::Files::new("/", "static")
                    .index_file("index.html")
                    // Static file cache control
                    .use_last_modified(true)
                )
        );
    };

    Ok(config.into())
}
